home *** CD-ROM | disk | FTP | other *** search
/ The CICA Windows Explosion! / The CICA Windows Explosion! - Disc 2.iso / demo / medowl.zip / SOURCE / MEDT.CPP < prev    next >
C/C++ Source or Header  |  1994-08-12  |  26KB  |  965 lines

  1. //----------------------------------------------------------------------------
  2. // ObjectWindows - (C) Copyright 1991, 1993 by Borland International
  3. //   source\owl\edit.cpp
  4. //   Implementation of class TEdit.  This defines the basic behavior
  5. //   of all edit controls.
  6. //----------------------------------------------------------------------------
  7. #pragma hdrignore SECTION
  8. #include <owl\owlpch.h>
  9. #ifdef MAGMA
  10. #include <owl\medt.h>
  11. #else
  12. #include <owl\edit.h>
  13. #endif
  14. #include <owl\validate.h>
  15. #include <dos.h>
  16. #include <string.h>
  17. #include <ctype.h>
  18.  
  19. #if !defined(SECTION) || SECTION == 1
  20.  
  21. //
  22. // Class pointer to an edit control that is trying to regain focus after losing
  23. // it with invalid contents. Is 0 in all other conditions.
  24. //
  25. TMagmaEdit* TMagmaEdit::ValidatorReFocus = 0;
  26.  
  27. DEFINE_RESPONSE_TABLE1(TMagmaEdit, TStatic)
  28.   EV_COMMAND(CM_EDITCUT, CmEditCut),
  29.   EV_COMMAND(CM_EDITCOPY, CmEditCopy),
  30.   EV_COMMAND(CM_EDITPASTE, CmEditPaste),
  31.   EV_COMMAND(CM_EDITDELETE, CmEditDelete),
  32.   EV_COMMAND(CM_EDITCLEAR, CmEditClear),
  33.   EV_COMMAND(CM_EDITUNDO, CmEditUndo),
  34.   EV_COMMAND_ENABLE(CM_EDITCUT, CmSelectEnable),
  35.   EV_COMMAND_ENABLE(CM_EDITCOPY, CmSelectEnable),
  36.   EV_COMMAND_ENABLE(CM_EDITDELETE, CmSelectEnable),
  37.   EV_COMMAND_ENABLE(CM_EDITPASTE, CmPasteEnable),
  38.   EV_COMMAND_ENABLE(CM_EDITCLEAR, CmCharsEnable),
  39.   EV_COMMAND_ENABLE(CM_EDITUNDO, CmModEnable),
  40.   EV_NOTIFY_AT_CHILD(EN_ERRSPACE, ENErrSpace),
  41.   EV_WM_CHAR,
  42.   EV_WM_KEYDOWN,
  43.   EV_WM_GETDLGCODE,
  44.   EV_WM_SETFOCUS,
  45.   EV_WM_KILLFOCUS,
  46.   EV_WM_CHILDINVALID,
  47. END_RESPONSE_TABLE;
  48.  
  49. static HMODULE hModMagmaEd = ::LoadLibrary("magmaed.dll");
  50.  
  51. //
  52. // constructor for a TMagmaEdit object
  53. //
  54. // by default, edit control has a border and its text is left-justified
  55. //
  56. // multiline edit control has horizontal vertical scroll bars
  57. //
  58. TMagmaEdit::TMagmaEdit(TWindow* parent,
  59.              int             id,
  60.              const char far* text,
  61.              int x, int y, int w, int h,
  62.              UINT            textLen,
  63.              BOOL            multiline,
  64.              TModule*        module)
  65.   : TStatic(parent, id, text, x, y, w, h, textLen, module)
  66. {
  67.   //
  68.   // undo the styles set by TStatic, & addin edit styles
  69.   //
  70.   Attr.Style &= ~SS_LEFT;
  71.   Attr.Style |= ES_LEFT | ES_AUTOHSCROLL | WS_BORDER | WS_TABSTOP;
  72.  
  73.   if (multiline)
  74.     Attr.Style |= ES_MULTILINE | ES_AUTOVSCROLL | WS_VSCROLL | WS_HSCROLL;
  75.   Validator = 0;
  76. }
  77.  
  78. //
  79. // constructor for TMagmaEdit associated with a MS-Windows interface element
  80. // created by MS-Windows from a resource definition
  81. //
  82. // by default, data transfer is enabled
  83. //
  84. TMagmaEdit::TMagmaEdit(TWindow*   parent,
  85.              int        resourceId,
  86.              UINT       textLen,
  87.              TModule*   module)
  88.   : TStatic(parent, resourceId, textLen, module)
  89. {
  90.   EnableTransfer();
  91.   Validator = 0;
  92. }
  93.  
  94. TMagmaEdit::~TMagmaEdit()
  95. {
  96.   delete (TStreamableBase*)Validator;
  97. }
  98.  
  99. //
  100. // beeps when edit control runs out of space
  101. //
  102. void
  103. TMagmaEdit::ENErrSpace()
  104. {
  105.   MessageBeep(0);
  106.  
  107.   DefaultProcessing();  // give parent a chance to process
  108. }
  109.  
  110. //
  111. // Responds to the GetDlgCode query according to the
  112. // current state of the control.  If the edit control
  113. // contains valid input, then TABs are allowed for
  114. // changing focus.  Otherwise, requests that TABs be
  115. // sent to Self, where we will generate the Invalid
  116. // message (See WMKeyDown below).
  117. //
  118. UINT
  119. TMagmaEdit::EvGetDlgCode(MSG far* msg)
  120. {
  121.   UINT retVal = (UINT)TStatic::EvGetDlgCode(msg);
  122.   if (!IsValid(FALSE))
  123.     retVal |= DLGC_WANTTAB;
  124.   return retVal;
  125. }
  126.  
  127. //
  128. // Validates Self whenever a character is entered.  Allows
  129. // the character entry to be processed normally, then validates
  130. // the result and restores Self's text to its original state
  131. // if there is an incorrect entry.
  132. //
  133. // By default, the SupressFill parameter of the IsValidInput
  134. // method call to the Validator is set to False, so that it
  135. // is free to modify the string, if it is so configured.
  136. //
  137. void
  138. TMagmaEdit::EvChar(UINT key, UINT repeatCount, UINT flags)
  139. {
  140. #if !defined(MAGMA)
  141.   if (Validator && key != VK_BACK) {
  142.     int oldBuffLen = GetTextLen();
  143.     char far* oldBuff = new char[oldBuffLen+1];
  144.     GetText(oldBuff, oldBuffLen+1);
  145.  
  146.     UINT   startSel, endSel;
  147.     GetSelection(startSel, endSel);
  148.     BOOL wasAppending = endSel == oldBuffLen;
  149.  
  150.     TStatic::EvChar(key, repeatCount, flags);      // Process the new char...
  151.  
  152.     GetSelection(startSel, endSel);
  153.     int buffLen = GetTextLen();
  154.     char far* buff = LockBuffer(max((int)TextLen,max(oldBuffLen,buffLen))+1);
  155.  
  156.     // Run the result of the edit through the validator.  If incorrect,
  157.     // then restore the original text.  Otherwise, range check & position
  158.     // the selection as needed.
  159.     //
  160.     if (!Validator->HasOption(voOnAppend) || wasAppending && endSel == buffLen) {
  161.       if (!Validator->IsValidInput(buff, FALSE)) {
  162.         strcpy(buff, oldBuff);
  163.       } else {
  164.         if (wasAppending)
  165.           startSel = endSel = strlen(buff); // may have autoFilled--move to end
  166.       }
  167.       UnlockBuffer(buff, TRUE);
  168.       SetSelection(startSel, endSel);
  169.  
  170.     } else {
  171.       if (endSel == buffLen && !Validator->IsValidInput(buff, FALSE))
  172.         Validator->Error();
  173.       UnlockBuffer(buff);
  174.     }
  175.     delete [] oldBuff;
  176.  
  177.   } else
  178. #endif
  179.     TStatic::EvChar(key, repeatCount, flags);
  180. }
  181.  
  182. //
  183. // If the TAB key is sent to the Edit Control, check the validity before
  184. // allowing the focus to change. The control will only get a TAB if
  185. // EvGetDlgCode(above) allows it, which is done when the control contains
  186. // invalid input (we re-validate here just for completeness, in case descendants
  187. // redefine any of this behavior).
  188. //
  189. // We need to validate on TAB focus-changes because there is a case not handled
  190. // by EvKillFocus: when focus is lost to an OK or CANCEL button by tabbing.
  191. //
  192. // Otherwise, for validators with the OnAppend option, perform an input
  193. // validation if the selection moves to the end. i.e. it becomes appending.
  194. //
  195. void
  196. TMagmaEdit::EvKeyDown(UINT key, UINT /*repeatCount*/, UINT /*flags*/)
  197. {
  198.   if (key == VK_TAB && !IsValid(TRUE))
  199.     return;
  200. #if !defined(MAGMA)
  201.   if (Validator && Validator->HasOption(voOnAppend)) {
  202.     UINT  startSel, endSel;
  203.     GetSelection(startSel, endSel);
  204.     int buffLen = GetTextLen();   // length of buffer
  205.     BOOL  wasAppending = endSel == buffLen;
  206.  
  207.     DefaultProcessing();
  208.  
  209.     if (!wasAppending) {
  210.       GetSelection(startSel, endSel);
  211.       char far* buff = LockBuffer();
  212.       if (endSel == strlen(buff) && !Validator->IsValidInput(buff, FALSE))
  213.         Validator->Error();
  214.       UnlockBuffer(buff);
  215.     }
  216.  
  217.   } else
  218. #endif
  219.     DefaultProcessing();
  220. }
  221.  
  222. //
  223. // Validates this whenever the focus is about to be lost.
  224. // Holds onto the focus if this is not valid.  Checks first
  225. // to make sure that the focus is not being taken by either
  226. // (a) another app, or (b) a Cancel button, or (c) an OK
  227. // button (in which case CanClose will validate); in each case,
  228. // we don't want to validate.
  229. //
  230. void
  231. TMagmaEdit::EvKillFocus(HWND hWndGetFocus)
  232. {
  233.   // If another validator is attempting to regain focus, then let it
  234.   //
  235.   if (Validator && !ValidatorReFocus) {
  236.     // The window belongs to us if any of the window handles has an object
  237.     // attached
  238.     //
  239.     HWND hWnd = hWndGetFocus;
  240.     while (hWnd && !GetWindowPtr(hWnd))
  241.       hWnd = ::GetParent(hWnd);
  242.  
  243.     if (hWnd) {
  244.       int btnId = ::GetDlgCtrlID(hWndGetFocus);
  245.  
  246.       // Note that we do not allow IsValid to post the message
  247.       // box, since the change of focus resulting from that message
  248.       // will interfere with the change we are in the process of
  249.       // completing.  Instead, post a message to the Parent informing
  250.       // it of the validation failure, and providing it with a handle
  251.       // to us.
  252.       //
  253.       if (btnId != IDCANCEL && btnId != IDOK && !IsValid(FALSE)) {
  254.         ValidatorReFocus = this;
  255.         Parent->PostMessage(WM_CHILDINVALID, WPARAM(HWindow));
  256.       }
  257.     }
  258.   }
  259.   TControl::EvKillFocus(hWndGetFocus);
  260. }
  261.  
  262. //
  263. // We need to make sure the anti-oscillation flag is cleared
  264. //
  265. void
  266. TMagmaEdit::EvSetFocus(HWND hWndLostFocus)
  267. {
  268.   // If we're getting focus back because of invalid input, then clear the
  269.   // anti-oscillation flag
  270.   //
  271.   if (ValidatorReFocus == this)
  272.     ValidatorReFocus = 0;
  273.  
  274.   TControl::EvSetFocus(hWndLostFocus);
  275. }
  276.  
  277.  
  278. //
  279. // handler for input validation message sent by parent
  280. //
  281. void
  282. TMagmaEdit::EvChildInvalid(HWND)
  283. {
  284.   ValidatorError();
  285. }
  286.  
  287.  
  288. void
  289. TMagmaEdit::ValidatorError()
  290. {
  291.   if (Validator) {
  292.     SetFocus();
  293.     Validator->Error();
  294.   }
  295. }
  296.  
  297. void
  298. TMagmaEdit::Clear()
  299. {
  300. #if defined(MAGMA)
  301.   DeleteSubText(0, -1L);
  302. #else
  303.   DeleteSubText(0, UINT(-1));
  304. #endif
  305. }
  306.  
  307. BOOL
  308. TMagmaEdit::CanClose()
  309. {
  310.   BOOL okToClose = TStatic::CanClose();
  311.   if (okToClose)
  312.     if (IsWindowEnabled() && !IsValid(TRUE)) {
  313.       ValidatorReFocus = this;
  314.       SetFocus();
  315.       return FALSE;
  316.     }
  317.   return okToClose;
  318. }
  319.  
  320. //
  321. // This function is called for Cut/Copy/Delete menu items to determine
  322. // whether or not the item is enabled.
  323. //
  324. void
  325. TMagmaEdit::CmSelectEnable(TCommandEnabler& commandHandler)
  326. {
  327.   UINT sPos, ePos;
  328.  
  329.   GetSelection(sPos, ePos);
  330.   commandHandler.Enable(sPos != ePos);
  331. }    
  332.  
  333. //
  334. // This function is called for the Paste menu item to determine whether or
  335. // not the item is enabled.
  336. //
  337. void
  338. TMagmaEdit::CmPasteEnable(TCommandEnabler& commandHandler)
  339. {
  340.   TClipboard clipboard(*this);
  341.   commandHandler.Enable(clipboard.IsClipboardFormatAvailable(CF_TEXT));
  342. }
  343.  
  344. //
  345. // This function is called for the Clear menu item to determine whether or
  346. // not the item is enabled.
  347. //
  348. void
  349. TMagmaEdit::CmCharsEnable(TCommandEnabler& commandHandler)
  350. {
  351.   commandHandler.Enable(!(GetNumLines() == 1 && GetLineLength(0) == 0));
  352. }
  353.  
  354. //
  355. // This function is called for the Undo menu item to determine whether or
  356. // not the item is enabled.
  357. //
  358. void
  359. TMagmaEdit::CmModEnable(TCommandEnabler& commandHandler)
  360. {
  361.   commandHandler.Enable(IsModified());
  362. }
  363.  
  364. //
  365. // returns the length of line number "LineNumber"
  366. //
  367. // if -1 is passed as the line number, the following applies:
  368. //   - returns the length of the line upon which the caret is positioned
  369. //   - if text is selected on the line, returns the line length minus the
  370. //     number of selected characters
  371. //   - if selected text spans more than one line, returns the length minus
  372. //     the number of selected characters
  373. //
  374. #if defined(MAGMA)
  375. int TMagmaEdit::GetLineLength(LONG lineNumber) const
  376. {
  377.   return (int)CONST_CAST(TMagmaEdit*,this)->HandleMessage(EM_LINELENGTH, 0,
  378.                                          (lineNumber > -1L) ? 
  379.                                             GetLineIndex(lineNumber) : -1L);
  380. }
  381. #else
  382. int TMagmaEdit::GetLineLength(int lineNumber) const
  383. {
  384.   return (int)CONST_CAST(TMagmaEdit*,this)->HandleMessage(EM_LINELENGTH,
  385.                                          (lineNumber > -1) ? 
  386.                                             GetLineIndex(lineNumber) : -1);
  387. }
  388. #endif
  389.  
  390. //
  391. // returns the text of line number "lineNumber" (0-terminated)
  392. // returns FALSE if an error occurs or if the text doesn't fit in "TextString"
  393. //
  394. BOOL
  395. #if defined(MAGMA)
  396. TMagmaEdit::GetLine(char far* textString, int strSize, LONG lineNumber) const
  397. #else
  398. TMagmaEdit::GetLine(char far* textString, int strSize, int lineNumber) const
  399. #endif
  400. {
  401.   if (strSize <= 0)
  402.     return FALSE;
  403.  
  404.   BOOL success = strSize >= GetLineLength(lineNumber) + 1;
  405.  
  406.   if (strSize < sizeof(int)) {
  407.     textString[0] = 0;
  408.     return success;
  409.   }
  410.   ((int far*)textString)[0] = strSize;
  411.  
  412.   int bytesCopied = (int)CONST_CAST(TMagmaEdit*,this)->
  413.                       HandleMessage(EM_GETLINE, lineNumber, (LPARAM)textString);
  414.  
  415.   textString[bytesCopied] = '\0'; // Windows returns non-0 terminated string
  416.  
  417.   if (bytesCopied != (int)CONST_CAST(TMagmaEdit*,this)->HandleMessage(EM_LINELENGTH,0,GetLineIndex(lineNumber)))
  418.     return FALSE;
  419.   return success;
  420. }
  421.  
  422. //
  423. // Lock the edit control's buffer, optionally resizing it first, and return
  424. // a far pointer to the beginning of it, or 0 if failure.
  425. // Must call UnlockBuffer when finished with buffer.
  426. //
  427. char far*
  428. TMagmaEdit::LockBuffer(UINT newSize)
  429. {
  430.   // make a copy if can't get handle. Single line edit controls or Win32s will
  431.   // fail the GetHandle(), so do it the hard way.
  432.   //
  433.   if (!(GetWindowLong(GWL_STYLE)&ES_MULTILINE)
  434. #if defined(__WIN32__)
  435.        || (::GetVersion()&0x80000000) && (::GetVersion()&0xFF) < 4  // Win32s
  436. #elif defined(MAGMA)
  437.        || 1
  438. #endif
  439.   ) {
  440.     if (!newSize)
  441.       newSize = GetTextLen()+1;
  442.     char* buffer = new char[newSize];
  443.     GetText(buffer, newSize);
  444.     return buffer;
  445.   }
  446.  
  447.   HLOCAL  hBuffer = GetHandle();
  448.  
  449.   #if defined(__WIN32__)
  450.     if (newSize) {
  451.       hBuffer = LocalReAlloc(hBuffer, newSize, LHND);
  452.       if (!hBuffer)
  453.         return 0;
  454.     }
  455.     return STATIC_CAST(char*,LocalLock(hBuffer));
  456.   #else
  457.     WORD editDS = FP_SEG(GlobalLock((HINSTANCE)GetWindowWord(GWW_HINSTANCE)));
  458.     asm push DS;
  459.     _DS = editDS;
  460.  
  461.     if (newSize) {
  462.       hBuffer = LocalReAlloc(hBuffer, newSize, LHND);
  463.       if (!hBuffer) {
  464.         asm pop DS;
  465.         return 0;
  466.       }
  467.     }
  468.     char far* returnValue = (char far*)MK_FP(_DS,LocalLock(hBuffer));
  469.     asm pop DS;
  470.     return returnValue;
  471.   #endif
  472. }
  473.  
  474. //
  475. // Unlock the edit control's buffer locked by LockBuffer.
  476. // Also informs control of new handle if indicated. Should update handle
  477. // if buffer is resized or written to.
  478. // Ignores call if buffer is 0
  479. //
  480. void
  481. TMagmaEdit::UnlockBuffer(const char far* buffer, BOOL updateHandle)
  482. {
  483.   if (!buffer)
  484.     return;
  485.  
  486.   // if a copy was made on lock, copy it back if requested & free buffer
  487.   //
  488.   if (!(GetWindowLong(GWL_STYLE)&ES_MULTILINE)
  489. #if defined(__WIN32__)
  490.        || (::GetVersion()&0x80000000) && (::GetVersion()&0xFF) < 4  // Win32s
  491. #elif defined(MAGMA)
  492.        || 1
  493. #endif
  494.    ) {
  495.     if (updateHandle)
  496.       SetText(buffer);
  497.     delete [] CONST_CAST(char far*,buffer);
  498.     return;
  499.   }
  500.  
  501.   #if defined(__WIN32__)
  502.     HLOCAL  hBuffer = LocalHandle((LPVOID)buffer);
  503.     LocalUnlock(hBuffer);
  504.   #else
  505.     WORD editDS = FP_SEG(buffer);
  506.     asm push DS;
  507.     _DS = editDS;
  508.  
  509.     HLOCAL  hBuffer = LocalHandle((void near*)FP_OFF(buffer));
  510.     LocalUnlock(hBuffer);
  511.  
  512.     asm pop DS;
  513.     GlobalUnlock((HGLOBAL)GlobalHandle(editDS));   // unlock LockBuffer's lock
  514.   #endif
  515.  
  516.   //
  517.   // handle may have moved or buffer contents written on
  518.   //
  519.   if (updateHandle)
  520.     SetHandle(hBuffer);
  521. }
  522.  
  523. //
  524. // Similar to strstr(), but is case sensitive or insensitive, uses Windows
  525. // string functions to work with different language drivers
  526. //
  527. static const char far*
  528. strstrcd(const char far* str1, const char far* str2, BOOL caseSens)
  529. {
  530.   PRECONDITION(str1 && str2);
  531.   int len2 = strlen(str2);
  532.   char far* p = (char far*)str1;
  533.   const char far* endp = str1 + strlen(str1) - len2 + 1;
  534.  
  535.   if (caseSens)
  536.     while (p < endp) {
  537.       char c = p[len2];            // must term p to match str2 len
  538.       p[len2] = 0;                 // for strcmp to work.
  539.       if (strcmp(p, str2) == 0) {
  540.         p[len2] = c;
  541.         return p;
  542.       }
  543.       p[len2] = c;
  544.       p++;
  545.     }
  546.   else
  547.     while (p < endp) {
  548.       char c = p[len2];
  549.       p[len2] = 0;
  550.       if (strcmpi(p, str2) == 0) {
  551.         p[len2] = c;
  552.         return p;
  553.       }
  554.       p[len2] = c;
  555.       p++;
  556.     }
  557.   return 0;
  558. }
  559.  
  560. //
  561. // Similar to strstrcd(), but searches backwards. Needs the length of str1
  562. // to know where to start.
  563. //
  564. static const char far*
  565. strrstrcd(const char far* str1, UINT len1, const char far* str2,
  566.            BOOL caseSens)
  567. {
  568.   PRECONDITION(str1 && str2);
  569.   int len2 = strlen(str2);
  570.   char far* p = (char far*)(str1 + len1 - len2);
  571.  
  572.   if (caseSens)
  573.     while (p >= str1) {
  574.       char c = p[len2];            // must term p to match str2 len
  575.       p[len2] = 0;                 // for strcmp to work.
  576.       if (strcmp(p, str2) == 0) {
  577.         p[len2] = c;
  578.         return p;
  579.       }
  580.       p[len2] = c;
  581.       p--;
  582.     }
  583.   else
  584.     while (p >= str1) {
  585.       char c = p[len2];
  586.       p[len2] = 0;
  587.       if (strcmpi(p, str2) == 0) {
  588.         p[len2] = c;
  589.         return p;
  590.       }
  591.       p[len2] = c;
  592.       p--;
  593.     }
  594.   return 0;
  595. }
  596.  
  597. //
  598. // searchs for and selects the given text and returns the offset of the text
  599. // or -1 if the text is not found
  600. //
  601. // if "startPos" = -1 then it is assumed that the start pos is the
  602. // end/beginning (depending on direction) of the current selection
  603. //
  604. #if defined(MAGMA)
  605. typedef struct tagSearchArg
  606. {
  607.   UINT fFlags;
  608. //  SEARCH_CASE_INSENSITIVE
  609. //  SEARCH_NO_REGEXP
  610. //  SEARCH_SELECTMATCH
  611. //  SEARCH_WHOLE_WORD_ONLY
  612.   char szPattern[256];
  613. } SEARCHARG, FAR *LPSEARCHARG;
  614. #endif
  615.  
  616. #if defined(MAGMA)
  617. LONG TMagmaEdit::Search(DWORD startPos, const char far* text, BOOL caseSensitive,
  618. #else
  619. int TMagmaEdit::Search(DWORD startPos, const char far* text, BOOL caseSensitive,
  620. #endif
  621.               BOOL wholeWord, BOOL up)
  622. {
  623.   if (!text || !text[0])
  624.     return -1;
  625.  
  626. #if defined(MAGMA)
  627.   SEARCHARG SearchArg = {  SEARCH_SELECTMATCH, { '\0' } };
  628.   if (!caseSensitive)
  629.     SearchArg.fFlags |= SEARCH_CASE_INSENSITIVE;
  630.   if (wholeWord)
  631.     SearchArg.fFlags |= SEARCH_WHOLE_WORD_ONLY;
  632.   lstrcpy(SearchArg.szPattern, text);
  633.  
  634.   // Don't get stuck at the old matched pattern
  635.   HandleMessage(up ? ME_MOVELEFT : ME_MOVERIGHT, 0, 0L);
  636.  
  637.   LONG result = (LONG) CONST_CAST(TMagmaEdit*,this)->HandleMessage(
  638.                       (up) ? ME_BSEARCH : ME_FSEARCH,
  639.                       (WPARAM) 0,
  640.                       (LPARAM) (LPSTR) &SearchArg);
  641.  
  642.   if (result >= 0)  // position in line
  643.   {
  644.     MAGMAED_STATUS meStatus;
  645.     HandleMessage(ME_QUERYSTATUS, 0, (LPARAM) (LPSTR) &meStatus);
  646.     result += GetLineIndex(meStatus.currLineNum-1);
  647.   }
  648.  
  649.   return result;
  650.  
  651. #else
  652.   if (startPos == (UINT)-1) {
  653.     UINT sBeg, sEnd;
  654.     GetSelection(sBeg, sEnd);
  655.     startPos = up ? sBeg : sEnd;
  656.   }
  657.   int textLen = strlen(text);
  658.  
  659.   //
  660.   // Lock the text buffer to get the pointer, and search thru it for the text
  661.   //
  662.   const char far* buffer = LockBuffer();
  663.   const char far* pos;
  664.   for (;;) {
  665.     if (up)
  666.       pos = strrstrcd(buffer, startPos, text, caseSensitive);
  667.     else
  668.       pos = strstrcd(buffer+startPos, text, caseSensitive);
  669.  
  670.     //
  671.     // If whole-word matching is enabled and we have a match so far, then make
  672.     // sure the match is on word boundaries.
  673.     //
  674.     if (wholeWord && pos) {
  675.       if (pos > buffer && isalnum(pos[-1]) || // Match is in preceding word
  676.           textLen < strlen(pos) && isalnum(pos[textLen])) {
  677.         startPos = (UINT)(pos-buffer) + !up;
  678.         continue;  // Skip this match and keep searching
  679.       }
  680.     }
  681.     break;  // Out of for loop
  682.   }
  683.  
  684.   // If we've got a match, select that text, cleanup & return.
  685.   //
  686.   if (pos) {
  687.     UINT sBeg = (UINT)(pos - buffer);
  688.     UnlockBuffer(buffer);
  689.     SetSelection(sBeg, sBeg + textLen);
  690. #if defined(__WIN32__)
  691.     HandleMessage(WM_KEYDOWN, VK_RIGHT);
  692.     SetSelection(sBeg, sBeg + textLen);
  693. #endif
  694.  
  695.     return sBeg;
  696.   }
  697.   UnlockBuffer(buffer);
  698.  
  699.   return -1;
  700. #endif
  701. }
  702.  
  703.  
  704. //
  705. // deletes the selected text; returns FALSE if no text is selected
  706. //
  707. BOOL
  708. TMagmaEdit::DeleteSelection()
  709. {
  710. #if defined(MAGMA)
  711.   CHARRANGE charRange;
  712.   ExGetSelection(&charRange);
  713.   if (charRange.dwStart != charRange.dwEnd) {
  714.     HandleMessage(WM_CLEAR);
  715.     return TRUE;
  716.   }
  717.   return FALSE;
  718. #else
  719.   UINT  startPos, endPos;
  720.   GetSelection(startPos, endPos);
  721.   if (startPos != endPos) {
  722.     HandleMessage(WM_CLEAR);
  723.     return TRUE;
  724.   }
  725.   return FALSE;
  726. #endif
  727. }
  728.  
  729. //
  730. // deletes text in the range "startPos .. endPos"
  731. //
  732. // returns FALSE if an error occurs
  733. //
  734. #if defined(MAGMA)
  735. BOOL TMagmaEdit::DeleteSubText(DWORD startPos, DWORD endPos)
  736. {
  737.   CHARRANGE charRange;
  738.   charRange.dwStart = startPos;
  739.   charRange.dwEnd   = endPos;
  740.   if (ExSetSelection(&charRange))
  741.     return DeleteSelection();
  742.   else
  743.     return FALSE;
  744. }
  745. #else
  746. BOOL TMagmaEdit::DeleteSubText(UINT startPos, UINT endPos)
  747. {
  748.   if (SetSelection(startPos, endPos))
  749.     return DeleteSelection();
  750.   else
  751.     return FALSE;
  752. }
  753. #endif
  754.  
  755. //
  756. // deletes the text at line number "lineNumber" (deletes the line containing
  757. // the caret if "lineNumber" is -1
  758. //
  759. // returns FALSE if "lineNumber" is greater than the number of lines
  760. //
  761. #if defined(MAGMA)
  762. BOOL TMagmaEdit::DeleteLine(LONG lineNumber)
  763. #else
  764. BOOL TMagmaEdit::DeleteLine(int lineNumber)
  765. #endif
  766. {
  767.   if (lineNumber == -1)
  768.     lineNumber = GetLineFromPos(GetLineIndex(-1));
  769.  
  770. #if defined(MAGMA)
  771.   LONG firstPos = GetLineIndex(lineNumber);
  772. #else
  773.   int firstPos = GetLineIndex(lineNumber);
  774. #endif
  775.  
  776.   if (firstPos != -1) {
  777. #if defined(MAGMA)
  778.     LONG lastPos = GetLineIndex(lineNumber + 1);
  779. #else
  780.     int  lastPos = GetLineIndex(lineNumber + 1);
  781. #endif
  782.  
  783.     if (lastPos == -1)
  784.       lastPos = firstPos + GetLineLength(lineNumber);
  785.  
  786.     if (firstPos == 0  && firstPos == lastPos) {
  787.       SetText("");
  788.       return TRUE;
  789.  
  790.     } else {
  791.       return DeleteSubText(firstPos, lastPos);
  792.     }
  793.   }
  794.  
  795.   return FALSE;
  796. }
  797.  
  798. //
  799. // retrieves the text of the associated edit control between the passed
  800. // positions
  801. //
  802. #if defined(MAGMA)
  803. void TMagmaEdit::GetSubText(char far* textString, DWORD startPos, DWORD endPos) const
  804. {
  805.   CHARRANGE charRange;
  806.   charRange.dwStart = startPos;
  807.   charRange.dwEnd   = endPos;
  808.   if (ExSetSelection(&charRange))
  809.     GetSelText(textString);
  810.   else
  811.     *textString = '\0';
  812. }
  813. #else
  814. void TMagmaEdit::GetSubText(char far* textString, UINT startPos, UINT endPos) const
  815. {
  816.   int  tempIndex = 0;
  817.  
  818.   if (endPos >= startPos) {
  819.     BOOL  okToContinue = TRUE;
  820.     int   startLine = GetLineFromPos(startPos);
  821.     int   endLine = GetLineFromPos(endPos);
  822.     int   startChar = startPos - GetLineIndex(startLine);
  823.     int   endChar = endPos - GetLineIndex(endLine);
  824.  
  825.     for (int tempLine = startLine; tempLine <= endLine && okToContinue; tempLine++) {
  826.       int    tempLineLength = GetLineLength(tempLine);
  827.       char*  line = new char [tempLineLength+1];
  828.       int    tempStart = tempLine == startLine ? startChar : 0;
  829.       int    tempEnd;
  830.  
  831.       if (tempLine == endLine)
  832.         tempEnd = (endChar > tempLineLength) ? tempLineLength : endChar;
  833.  
  834.       else
  835.         tempEnd = tempLineLength;
  836.  
  837.       int  tempSize = tempEnd - tempStart;
  838.  
  839.       if (GetLine(line, tempLineLength + 1, tempLine)) {
  840.         //
  841.         // can happen if we're indexing the CR/LF
  842.         //
  843.         if (tempSize > 0) {
  844.           memcpy(&textString[tempIndex], &line[tempStart], tempSize);
  845.           tempIndex += tempSize;
  846.         }
  847.  
  848.         if (tempLine != endLine) {
  849.           textString[tempIndex++] = 0x0d;  // CR
  850.           textString[tempIndex++] = 0x0a;  // LF
  851.         }
  852.  
  853.       } else
  854.         okToContinue = FALSE;
  855.  
  856.       delete [] line;
  857.     }
  858.   }
  859.   textString[tempIndex] = '\0';
  860. }
  861. #endif
  862.  
  863. //
  864. // Return name of predefined Windows edit class
  865. //
  866. char far*
  867. TMagmaEdit::GetClassName()
  868. {
  869.   if (hModMagmaEd > (HMODULE) 32)
  870.     return "MAGMAEDIT";
  871.   else
  872.     return "EDIT";
  873. }
  874.  
  875. //
  876. // limits the amount of text that an edit control can have to the value of
  877. // TextLen
  878. //
  879. void
  880. TMagmaEdit::SetupWindow()
  881. {
  882.   TStatic::SetupWindow();
  883.  
  884.   if (TextLen != 0)
  885.     HandleMessage(EM_LIMITTEXT, TextLen - 1);
  886. }
  887.  
  888. BOOL
  889. TMagmaEdit::IsValid(BOOL reportError)
  890. {
  891. #if !defined(MAGMA)
  892.   if (Validator) {
  893.     char far* buffer = LockBuffer();
  894.     BOOL valid = reportError ? Validator->Valid(buffer) : 
  895.                                Validator->IsValid(buffer);
  896.     UnlockBuffer(buffer);
  897.     return valid;
  898.   }
  899. #endif
  900.   return TRUE;
  901. }
  902.  
  903. void
  904. TMagmaEdit::SetValidator(TValidator* validator)
  905. {
  906.   delete Validator;
  907.   Validator = validator;
  908. }
  909.  
  910. //
  911. // transfers state information for TMagmaEdit controls
  912. //
  913. // delegates to the Validator if there is one & it has the transfer option set,
  914. // allowing the Validator to convert the text to/from the appropriate type.
  915. // else passes to base, TStatic.
  916. //
  917. // the return value is the size (in bytes) of the transfer data
  918. //
  919. UINT
  920. TMagmaEdit::Transfer(void* buffer, TTransferDirection direction)
  921. {
  922.   if (Validator && Validator->HasOption(voTransfer) && GetNumLines() <= 1) {
  923.     CHECK(GetWindowTextLength() < TextLen);
  924.     char* text = new char[TextLen];
  925.     GetText(text, TextLen);
  926.     UINT result = Validator->Transfer(text, buffer, direction);
  927.     if (result == 0)
  928.       result = TStatic::Transfer(buffer, direction);
  929.     else if (direction == tdSetData)
  930.       SetText(text);
  931.     delete [] text;
  932.     return result;
  933.   }
  934.   return TStatic::Transfer(buffer, direction);
  935. }
  936.  
  937. #endif
  938.  
  939. #if !defined(SECTION) || SECTION == 2
  940.  
  941. IMPLEMENT_STREAMABLE1(TMagmaEdit, TStatic);
  942.  
  943. //
  944. // reads an instance of TMagmaEdit from the passed ipstream
  945. //
  946. void*
  947. TMagmaEdit::Streamer::Read(ipstream& is, uint32 /*version*/) const
  948. {
  949.   ReadBaseObject((TStatic*)GetObject(), is);
  950.   is >> GetObject()->Validator;
  951.   return GetObject();
  952. }
  953.  
  954. //
  955. // writes the TMagmaEdit to the passed opstream
  956. //
  957. void
  958. TMagmaEdit::Streamer::Write(opstream& os) const
  959. {
  960.   WriteBaseObject((TStatic*)GetObject(), os);
  961.   os << GetObject()->Validator;
  962. }
  963.  
  964. #endif
  965.